using Sandia.MVCFramework.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;

namespace Sandia.MVCFramework.Plugin
{
    /// <summary>
    /// A class that searches the subdirectories of the given directory for 
    /// IPlugins.
    /// </summary>
    [Serializable]
    public class StandardPluginFinder : IPluginFinder
    {
        #region IPluginFinder Members

        /// <summary>
        /// Searches the subdirectories of the given directory for IPlugins.
        /// </summary>
        /// <param name="dirInfo">The directory to search.</param>
        /// <returns>
        /// A mapping of IPlugin types to dll files which contain them.
        /// </returns>
        public IDictionary<Type, FileInfo> Search(
            DirectoryInfo dirInfo)
        {
            Dictionary<Type, FileInfo> plugins = 
                new Dictionary<Type, FileInfo>();
            // Loop through each subdirectory in the plugin directory.
            foreach (DirectoryInfo pluginDirInfo in dirInfo.GetDirectories())
            {
                // Loop through each DLL in the subdirectory.
                foreach (FileInfo fileInfo in pluginDirInfo.GetFiles("*.dll"))
                {
                    try
                    {
                        // Load the DLL assembly and initialize it
                        Assembly assembly = Assembly.LoadFrom(fileInfo.FullName);
                        // Loop through each of the types in the assembly and
                        // check to see if it is a plugin.
                        foreach (Type assemblyType in assembly.GetTypes())
                        {
                            if (typeof(IPlugin).IsAssignableFrom(assemblyType))
                            {
                                // If it's a plugin, add it to the list.
                                plugins[assemblyType] = fileInfo;
                                // Don't need to keep searching once we have found one.
                                break;
                            }
                        }
                    }
                    catch (ReflectionTypeLoadException e)
                    {
                        string exceptionText = e.Message;
                        foreach (Exception loaderException in e.LoaderExceptions)
                        {
                            exceptionText = exceptionText + "\n" + loaderException.Message;
                        }

                        // Generate a log entry
                        LoggingTools.SendToLog(
                            "Search: ReflectionTypeLoadException - " + exceptionText,
                            LoggingCategories.GENERAL,
                            50);
                    }
                }
            }
            return plugins;
        }

        #endregion
    }
}
